package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/grpc/gen/proxy2/bilinadmin"
	"bgs/grpc/imp/constants"
	"bgs/service/model"
	"bgs/util"
	"context"
	"encoding/json"
	"strconv"
	"time"
)

// OpenNewParam 开通新品牌参数
type OpenNewParam struct {
	UserID             int64
	UserNickName       string
	UserAccountToken   string
	Name               string
	Intro              string
	Pic                string
	RemotePkOfBrand    int64
	RemotePkOfEmployee int64 // 用户accountToken对应在比邻品牌下的emp_id
}

// OpenNew 开通新品牌
func (s *BrandServiceImp) OpenNew(ctx context.Context, q *sqlc.Queries, ptr *OpenNewParam) (brand sqlc.Brand, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	remotePkOfBrand := ptr.RemotePkOfBrand
	remotePkOfEmployee := ptr.RemotePkOfEmployee
	txTime := time.Now()

	var syncAction sqlc.EmSyncAction
	var origin sqlc.EmDataSource
	var bilinBrand model.BilinBrand
	if ptr.RemotePkOfBrand == 0 {
		origin = sqlc.EmDataSourceLocal
		syncAction = sqlc.EmSyncActionLCR // 本地发起创建并同步到比邻
		// 远程需要创建品牌并同步映射到比邻品牌
		bilinBrand, err = s.createBilinBrand(ctx, ptr)
		if err != nil {
			return
		}
		remotePkOfBrand = bilinBrand.RemotePkOfBrand
		remotePkOfEmployee = bilinBrand.RemotePkOfEmployee
		slog.Info("1 创建比邻品牌 ok")
	} else {
		// 比邻品牌已存在,仅在本地创建并同步映射到比邻品牌
		origin = sqlc.EmDataSourceRemote
		syncAction = sqlc.EmSyncActionRCL // 远程创建并在本地开通星团
		bilinBrand, err = s.getBilinBrand(ctx, ptr.UserAccountToken, ptr.RemotePkOfBrand)
		if err != nil {
			return
		}
		slog.Info("1 无需创建比邻品牌 ok")
	}

	// 创建品牌
	brand, err = q.CreateBrand(ctx, sqlc.CreateBrandParams{
		Origin:   string(origin),
		Name:     ptr.Name,
		Intro:    sqlc.NSFromString(ptr.Intro),
		Pic:      sqlc.NSFromString(ptr.Pic),
		RemotePk: remotePkOfBrand,
	})
	if err != nil {
		return
	}
	slog.Info("2 创建品牌(本地) ok")

	brandID := brand.ID

	// 创建品牌管理员 这里需要将remotePkOfEmployee同步近brandAdmin里
	_, err = q.UpsertBrandAdmin(ctx, sqlc.UpsertBrandAdminParams{
		UserID:    ptr.UserID,
		BrandID:   brandID,
		AliasName: ptr.UserNickName,
		Became:    txTime,
		RemotePk:  remotePkOfEmployee,
	})
	if err != nil {
		return
	}
	slog.Info("3 创建品牌管理员 ok", remotePkOfEmployee)

	err = s.UpdateBrandStatAdminCountSinceUpsertBrandAdmin(ctx, q, brandID)
	if err != nil {
		return
	}
	slog.Info("3.1 统计品牌admin_count ok")

	// 设置品牌的所有者
	_, err = q.UpsertBrandExt(ctx, sqlc.UpsertBrandExtParams{
		BrandID:   brandID,
		OwnUserID: ptr.UserID,
	})
	if err != nil {
		return
	}
	slog.Info("4 设置品牌的所有者 ok")

	// 设置同步
	localObj, localPkName, localFields := constants.SyncLocalObjBrand, constants.SyncLocalPkNameBrand, constants.SyncLocalFieldsBrand
	remoteObj, remotePkName, remoteFields := constants.SyncRemoteObjBrand, constants.SyncRemotePkNameBrand, constants.SyncRemoteFieldsBrand

	sync, err := q.UpsertSync(ctx, sqlc.UpsertSyncParams{
		LocalObj:         localObj,
		LocalPkName:      localPkName,
		LocalFields:      localFields,
		RemoteObj:        remoteObj,
		RemotePkName:     remotePkName,
		RemoteFields:     remoteFields,
		LatestSynced:     txTime,
		LatestSyncAction: string(syncAction),
	})
	if err != nil {
		return
	}
	slog.Info("5 品牌同步 ok")

	localPK := strconv.FormatInt(brand.ID, 10)
	// 同步映射
	syncMapping, err := q.UpsertSyncMapping(ctx, sqlc.UpsertSyncMappingParams{
		SyncID:    sync.ID,
		LocalObj:  localObj,
		LocalPk:   localPK,
		RemoteObj: remoteObj,
		RemotePk:  strconv.FormatInt(remotePkOfBrand, 10),
	})
	if err != nil {
		return
	}
	slog.Info("6 品牌同步映射 ok")

	// 同步日志
	syncData, err := json.Marshal(struct {
		ID   int64  `json:"id"`
		Name string `json:"name"`
	}{
		ID:   brand.ID,
		Name: brand.Name,
	})
	if err != nil {
		return
	}

	maxVer, err := q.GetSyncLogMaxVer(ctx, sqlc.NInt64FromInt64(syncMapping.ID))
	if err != nil {
		return
	}
	_, err = q.CreateSyncLog(ctx, sqlc.CreateSyncLogParams{
		SyncID:        sync.ID,
		SyncMappingID: sqlc.NInt64FromInt64(syncMapping.ID),
		LocalObj:      localObj,
		LocalPk:       localPK,
		RemoteObj:     remoteObj,
		RemotePk:      strconv.FormatInt(remotePkOfBrand, 10),
		Synced:        txTime,
		Action:        string(syncAction),
		Data:          syncData,
		SyncStatus:    string(sqlc.EmSyncStatusDone),
		Ver:           int64(maxVer + 1),
	})
	if err != nil {
		return
	}
	slog.Info("7 品牌同步日志 ok")

	// 5 设置同步远程对象
	_, err = q.UpsertSyncRemote(ctx, sqlc.UpsertSyncRemoteParams{
		SyncID:    sync.ID,
		RemoteObj: remoteObj,
		RemotePk:  strconv.FormatInt(remotePkOfBrand, 10),
		Data:      bilinBrand.Raw,
	})

	if err != nil {
		return
	}
	slog.Info("8 品牌同步远程对象 ok")
	return
}

type ListUnmappingedBilinBrandsParam struct {
	UserID           int64
	UserAccountToken string
}

// ListUnmappingedBilinBrands 获取未映射到星团的比邻品牌列表
func (s *BrandServiceImp) ListUnmappingedBilinBrands(ctx context.Context, q *sqlc.Queries, p ListUnmappingedBilinBrandsParam) (unmappingedBilinBrands []model.BilinBrand, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	bilinBrandsOfAccount, err := s.listBilinBrands(ctx, p.UserAccountToken)
	if err != nil {
		return
	}
	slog.Info("1 获取accountToken下的比邻品牌列表 ok")
	slog.Infof("bilinBrandsOfAccount:%+v", bilinBrandsOfAccount)

	brands, err := q.ListBrands(ctx)
	if err != nil {
		return
	}
	slog.Info("2 获取星团品牌列表 ok")

	sgIDs := util.MapToInt64(util.ToGenericSlice(brands), func(v interface{}) int64 {
		val := v.(sqlc.Brand)
		return val.RemotePk
	})

	for _, item := range bilinBrandsOfAccount {
		if !util.Int64Contains(sgIDs, item.RemotePkOfBrand) {
			unmappingedBilinBrands = append(unmappingedBilinBrands, item)
		}
	}
	slog.Info("3 获取未映射到星团的比邻品牌列表 ok")
	return
}

// createBilinBrand 创建比邻品牌
func (s *BrandServiceImp) createBilinBrand(ctx context.Context, ptr *OpenNewParam) (bilinBrand model.BilinBrand, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	req := &bilinadmin.BrandCreateRequest{
		Logo:         ptr.Pic,
		Name:         ptr.Name,
		Desc:         ptr.Intro,
		BizType:      bilinadmin.BizTypeEnum_BizTypeSelfRun,
		AccountToken: ptr.UserAccountToken,
	}

	slog.Infof("bilinadmin.BrandCreate req:%v", req)

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	reply, err := s.proxy.BilinAdminCl.BrandCreate(ctxWithTimout, req)
	if err != nil {
		return
	}
	bilinBrand.RemotePkOfBrand = reply.BrandId
	bilinBrand.RemotePkOfEmployee = reply.EmpId
	bilinBrand.Name = reply.Name
	bilinBrand.Logo = reply.Logo
	bilinBrand.Desc = reply.Desc

	bytesOfBrand, err := json.Marshal(reply)
	if err != nil {
		return
	}
	bilinBrand.Raw = json.RawMessage(bytesOfBrand)
	return
}

func (s *BrandServiceImp) listBilinBrands(ctx context.Context, accountToken string) (bilinBrands []model.BilinBrand, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	req := &bilinadmin.BrandsGetRequest{
		AccountToken: accountToken,
	}
	slog.Infof("bilinadmin.BrandsGet req:%v", req)
	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	reply, err := s.proxy.BilinAdminCl.BrandsGet(ctxWithTimout, req)
	if err != nil {
		return
	}
	brandResponses := reply.GetBrands()
	var bytesOfBrand []byte
	for _, bres := range brandResponses {
		bytesOfBrand, err = json.Marshal(reply)
		if err != nil {
			return
		}
		bilinBrands = append(bilinBrands, model.BilinBrand{
			RemotePkOfBrand:    bres.BrandId,
			RemotePkOfEmployee: bres.EmpId,
			Name:               bres.Name,
			Logo:               bres.Logo,
			Desc:               bres.Desc,
			Raw:                json.RawMessage(bytesOfBrand),
		})
	}

	return
}

func (s *BrandServiceImp) getBilinBrand(ctx context.Context, accountToken string, remotePkOfBrand int64) (bilinBrand model.BilinBrand, err error) {

	bilinBrands, err := s.listBilinBrands(ctx, accountToken)
	if err != nil {
		return
	}
	for _, item := range bilinBrands {
		if item.RemotePkOfBrand == remotePkOfBrand {
			bilinBrand = item
			return
		}
	}
	return
}
